home *** CD-ROM | disk | FTP | other *** search
/ Turnbull China Bikeride / Turnbull China Bikeride - Disc 1.iso / ARGONET / PD / SOUND / REPLAYER.SPK / c / playback < prev    next >
Text File  |  1998-09-04  |  22KB  |  677 lines

  1.  
  2. /* playback.c
  3.  
  4.    Replayer -- audio player
  5.    Copyright (c) 1997 Mark Seaborn <mseaborn@argonet.co.uk>
  6.  
  7.    Contains the functions for calling the Acorn Replay sound drivers to
  8.    produce sound output.  This file is not portable unless it is re-written
  9.    for different sets of drivers, and is the main non-portable component
  10.    of Replayer.
  11. */
  12.  
  13. #include <string.h>
  14. #include <time.h>
  15.  
  16. #include "swis.h"
  17. #include "OS:os.h"
  18. #include "OS:osbyte.h"
  19. #include "OS:osfile.h"
  20. #include "OS:osmodule.h"
  21. #include "OS:osfind.h"
  22. #include "OS:osgbpb.h"
  23.  
  24. #ifdef MemCheck_MEMCHECK
  25. #include "MemCheck:MemCheck.h"
  26. #endif
  27.  
  28. #include "replayer.h"
  29. #include "replaydriver.h"
  30.  
  31.  
  32. /* Functions for finding information about particular Acorn Replay
  33.    sound drivers */
  34.  
  35. /* Gets information about a type 2 sound driver when given the leafname of
  36.    the directory that the driver resides in.  You must free the structure it
  37.    returns yourself.  Returns 0 for failure. */
  38. replayer_sound_driver_info *replayer_get_driver_info_type2(const char *name)
  39. {
  40.   /* The variables we need. */
  41.   replayer_sound_driver_info *info = 0;
  42.   char *filename = 0;
  43.   FILE *file = 0;
  44.   if(!name) return 0;
  45.  
  46.   /* Allocate the info structure. */
  47.   info = malloc(sizeof(replayer_sound_driver_info));
  48.   if(!info) goto fail;
  49.  
  50.   /* Open the "Info" file for the driver. */
  51.   filename = malloc(strlen(replaydriver_DRIVER_PREFIX) + strlen(name) + 10);
  52.   if(!filename) goto fail;
  53.   strcpy(filename, replaydriver_DRIVER_PREFIX);
  54.   strcat(filename, name);
  55.   strcat(filename, ".Info");
  56.   file = fopen(filename, "r");
  57.   if(!file) goto fail;
  58.  
  59.   /* Read the first two text lines. */
  60.   info->name = replayer_read_header_line(file);
  61.   info->copyright = replayer_read_header_line(file);
  62.  
  63.   /* Read flag and precision. */
  64.   info->start_anywhere = replayer_read_header_number(file);
  65.   info->precision = replayer_read_header_number(file);
  66.  
  67.   /* Read the final three specialised values. */
  68.   info->variable_ratio = replayer_read_header_number(file);
  69.   {
  70.     char *line = replayer_read_header_line(file);
  71.     info->max_sample_bits = atof(line);
  72.     free(line);
  73.   }
  74.   info->buffer_overhead = replayer_read_header_number(file);
  75.  
  76.   /* Return successfully. */
  77.   return info;
  78.  
  79.   /* Return with failure; free up things. */
  80. fail:
  81.   free(info);
  82.   free(filename);
  83.   if(file) fclose(file);
  84.   return 0;
  85. }
  86.  
  87. /* Returns information about the sound driver needed to play the sound in
  88.    the soundtrack given.  If the soundtrack is type 2, it calls
  89.    replayer_get_driver_info_type().  Returns 0 for failure.  You must
  90.    free everything yourself. */
  91. replayer_sound_driver_info *replayer_get_driver_info_from_soundtrack
  92.     (const replayer_soundtrack_info *tinfo)
  93. {
  94.   if(!tinfo) return 0;
  95.  
  96.   switch(tinfo->type_number) {
  97.     /* Type 1 sound formats. */
  98.   case 1: {
  99.     replayer_sound_driver_info *info;
  100.     const char *name, *copyright;
  101.  
  102.     /* Allocate the info block to return. */
  103.     info = malloc(sizeof(replayer_sound_driver_info));
  104.     if(!info) return 0;
  105.  
  106.     /* Default values to fill in (might be slightly wrong). */
  107.     name = "Type 1, vanilla sound";
  108.     copyright = "Type 1 driver, copyright unknown";
  109.     info->copyright = malloc(strlen(copyright)+1);
  110.     if(info->copyright) strcpy(info->copyright, copyright);
  111.     info->precision = 8;
  112.     info->start_anywhere = 1;
  113.     info->variable_ratio = 0;
  114.     info->max_sample_bits = 0; /* Unknown. */
  115.     info->buffer_overhead = 0; /* N/A. */
  116.  
  117.     /* Alter the defaults for ADPCM sound. */
  118.     if((tinfo->precision != 16 && !tinfo->linear_sound) &&
  119.        (tinfo->precision == 4 || tinfo->adpcm_sound)) {
  120.       name = "Type 1, ADPCM sound";
  121.       info->precision = 16;
  122.       info->start_anywhere = 0;
  123.       info->variable_ratio = 1;
  124.     }
  125.  
  126.     /* Copy the name across and return. */
  127.     info->name = malloc(strlen(name)+1);
  128.     if(info->name) strcpy(info->name, name);
  129.     return info; }
  130.  
  131.     /* Type 2: could be anything else. */
  132.   case 2:
  133.     return replayer_get_driver_info_type2(tinfo->type);
  134.  
  135.     /* Something we don't recognise; just fail. */
  136.   default:
  137.     return 0;
  138.   }
  139. }
  140.  
  141. /* Frees up a driver info structure and all the values in it. */
  142. void replayer_free_driver_info(replayer_sound_driver_info *info)
  143. {
  144.   if(!info) return;
  145.  
  146.   free(info->name);
  147.   free(info->copyright);
  148.   free(info);
  149. }
  150.  
  151.  
  152. /* Methods for finding the filename of the driver you want */
  153.  
  154. /* This method returns the filename of the driver file which contains the
  155.    code necessary for playing this particular Replay soundtrack.  You must
  156.    free the string it returns yourself.  It will return zero if it fails. */
  157. char *replayer_get_driver_filename(const replayer_soundtrack_info *info)
  158. {
  159.   /* Variables we need. */
  160.   char *filename;
  161.   if(!info) return 0;
  162.  
  163.   /* Allocate the filename and copy the initial prefix. */
  164.   filename = malloc(strlen(replaydriver_DRIVER_PREFIX) + 24);
  165.   if(!filename) return 0;
  166.   strcpy(filename, replaydriver_DRIVER_PREFIX);
  167.  
  168.   switch(info->type_number) {
  169.     /* Type 1 sound format. */
  170.   case 1:
  171.     strcat(filename, "Sound");
  172.     if(info->precision == 16 || info->linear_sound) {
  173.       if(info->unsigned_sound)
  174.         strcat(filename, "U");
  175.         else strcat(filename, "S");
  176.     }
  177.     else {
  178.       if(info->precision == 4 || info->adpcm_sound)
  179.         strcat(filename, "A");
  180.         else strcat(filename, "E");
  181.     }
  182.     sprintf(filename + strlen(filename), "%i", info->precision);
  183.     break;
  184.  
  185.     /* Type 2: could be anything else. */
  186.   case 2: {
  187.     /* Finish off the driver's filename.  The leafname of the driver's
  188.        directory has already been extracted for us! */
  189.     strcat(filename, info->type);
  190.     strcat(filename, ".Play");
  191.     break; }
  192.  
  193.     /* We don't recognise the sound type. */
  194.   default:
  195.     free(filename);
  196.     return 0;
  197.   }
  198.  
  199.   /* Add the number of channels to the filename. */
  200.   if(info->channels > 1)
  201.     sprintf(filename + strlen(filename), "x%i", info->channels);
  202.  
  203.   /* And return our filename... */
  204.   return filename;
  205. }
  206.  
  207. /* This method just calls replayer_get_driver_filename() but takes a
  208.    Replayer file and a soundtrack number, and validates that the track
  209.    exists first.  Will return zero if it fails. */
  210. char *replayer_obj_get_driver_filename(const replayer_file *obj, int track)
  211. {
  212.   const replayer_header *header;
  213.   if(!obj) return 0;
  214.   header = replayer_get_header(obj);
  215.   if(!header) return 0;
  216.  
  217.   if(track >= header->no_soundtracks) return 0;
  218.     else return replayer_get_driver_filename(&header->soundtracks[track]);
  219. }
  220.  
  221.  
  222. /* Public functions for sound playback */
  223.  
  224. /* Starts sound-only playback on the Replay file.  Returns non-zero for
  225.    failure (see header file for reason codes).  You must tell it which
  226.    soundtrack in the file to use (this will usually be 0). */
  227. int replayer_sound_play(replayer_file *obj, int track)
  228. {
  229.   replayer_sound_driver_info *driver_info = 0;
  230.   replayer_soundtrack_info *info;
  231.   replaydriver_timecheck_state time_check;
  232.   int rc = -1; /* Return reason code */
  233.   if(!obj) return -1; /* Failure */
  234.  
  235.   /* Find the driver needed and load it, if it's not already loaded. */
  236.   if(!obj->driver) {
  237.     char *driver_filename = replayer_obj_get_driver_filename(obj, track);
  238.     if(!driver_filename) { rc = replayer_play_FORMAT_ERROR; goto fail; }
  239.     obj->driver = replaydriver_load(driver_filename);
  240.     free(driver_filename);
  241.     if(!obj->driver) { rc = replayer_play_DRIVER_ERROR; goto fail; }
  242.   }
  243.   /* Allocate a control block if it hasn't been allocated already. */
  244.   if(!obj->control) {
  245.     obj->control = replaydriver_control_create();
  246.     if(!obj->control) { rc = replayer_play_ALLOC_ERROR; goto fail; }
  247.   }
  248.  
  249.   /* We know the soundtrack number is okay now and the header is there. */
  250.   info = &obj->header->soundtracks[track];
  251.  
  252.   /* If playing backwards, ensure that it can be done with this
  253.      sound type. */
  254. #ifndef replayer_NO_BACKWARDS
  255.   if(obj->backwards) {
  256.     if(info->type_number != 1 ||
  257.       (info->precision != 8 && info->precision != 16) ||
  258.       info->adpcm_sound) { rc = replayer_play_FORMAT_ERROR; goto fail; }
  259.   }
  260. #endif
  261.  
  262.   /* Fill in the control block. */
  263.   obj->control->data.flags = 0;
  264.   obj->control->data.skip_late_data = obj->skip_late_data;
  265.   replaydriver_set_rate(obj->control, info->rate);
  266.   obj->control->data.quality = obj->quality;
  267.   obj->control->data.reversed = info->reverse_channels;
  268.  
  269.   /* Start the timing check. */
  270.   replaydriver_timecheck_start(obj->driver, obj->control, &time_check);
  271.   /* We are now technically playing. */
  272. #ifndef replayer_NO_BACKWARDS
  273.   if(obj->backwards) obj->current_chunk = obj->header->no_chunks;
  274.     else /* Drop through */
  275. #endif
  276.   obj->current_chunk = 0;
  277.   obj->track_playing = track;
  278.   obj->playing = 1;
  279.  
  280.   /* Get the information about the driver.  We need to know the number of
  281.      sound storage bits for later on. */
  282.   driver_info = replayer_get_driver_info_from_soundtrack(info);
  283.   if(!driver_info)
  284.     { rc = replayer_play_DRIVER_ERROR; goto fail_in_soundtest; }
  285.  
  286.   /* Allocate space for the sound buffers. */
  287.   if(!obj->buffer) {
  288.     /* Find the amount of storage needed for each buffer. */
  289.     size_t buffer_size = ((size_t)
  290.       ((double) obj->header->frames_per_chunk /
  291.           (double) obj->header->fps    /* Find seconds per chunk. */
  292.     * info->rate        /* Account for sample rate. */
  293.     * info->channels    /* Account for number of channels. */
  294.     * 1.01            /* Add 1% extra space. */
  295.     * driver_info->precision /* Number of bits per sample. */
  296.     + 7) / 8        /* Round up and convert bits -> bytes. */
  297.     + 12 + 4        /* Add some more. */
  298.     + 3) & ~3;        /* Word align it. */
  299.  
  300.     obj->buffer = replaydriver_buffer_create(buffer_size, obj->driver);
  301.     if(!obj->buffer)
  302.       { rc = replayer_play_ALLOC_ERROR; goto fail_in_soundtest; }
  303.   }
  304.   /* Presumably the driver uses up buffer 0 first. */
  305.   obj->next_buffer = 0;
  306.  
  307. #if 0
  308.     /* Allocate space for the buffers. */
  309.     total_size = (buffer_size + 4) * 2;
  310.     {
  311.       int os_version;
  312.       os_byte(129, 0, 0xFF, &os_version, 0);
  313.       if(os_version >= 0xA5) {
  314.         /* Running under RISC OS 3.5+, so use a dynamic area. */
  315.         if(xosdynamicarea_create(-1, total_size, (byte *) -1, 1<<7,
  316.         total_size * 4 /* Max size */,
  317.         0, 0, DYNAMIC_AREA_NAME,
  318.         &obj->sound_buffer_dynarea, (byte **) &buffers, 0))
  319.             goto use_rma_buffer;
  320.       }
  321.       else {
  322.         /* Dynamic areas not available, so allocate from module area. */
  323.       use_rma_buffer:
  324.         if(xosmodule_alloc(total_size, (void **) &buffers) || !buffers)
  325.           goto fail_in_soundtest;
  326.         obj->sound_buffer_dynarea = -1;
  327.       }
  328.     }
  329.     obj->sound_buffer = buffers;
  330.     obj->sound_buffer_size = buffer_size;
  331.     /* Register the buffers with MemCheck. */
  332. #ifdef MemCheck_MEMCHECK
  333.     MemCheck_RegisterMiscBlock(buffers, total_size);
  334. #endif
  335.     buffer1 = buffers;
  336.     buffer2 = buffers + buffer_size + 4;
  337.  
  338.     /* Tell the driver where the buffer is. */
  339.     replaydriver_set_buffer(obj->driver, 0, buffer1);
  340.     replaydriver_set_buffer(obj->driver, 1, buffer2);
  341.     /* Set both buffers to unfilled. */
  342.     ((int *) buffer1)[1] = 1;
  343.     ((int *) buffer2)[1] = 1;
  344.     /* Put the check word at the end of both buffers. */
  345.     *((int *) (buffer1 + buffer_size)) = BUFFER_CHECK_WORD;
  346.     *((int *) (buffer2 + buffer_size)) = BUFFER_CHECK_WORD;
  347.   }
  348. #endif
  349.   /* The sound driver info can now be freed. */
  350.   replayer_free_driver_info(driver_info);
  351.   driver_info = 0;
  352.  
  353.   /* Open the Replay file so we can slurp in data from it. */
  354.   /* We use the OS calls rather than stdio because stdio's buffering may
  355.      get in the way and slow things down.  We use _swix rather than OSLib
  356.      because OSLib's os_f type is only byte-sized, which will break things
  357.      when filing systems use file handles >255. */
  358.   if(_swix(OS_Find, _IN(0)|_IN(1)|_OUT(0), 0x40, obj->filename, &obj->file)
  359.     || !obj->file)
  360.     { rc = replayer_play_FILE_ERROR; goto fail_in_soundtest; }
  361.  
  362.   /* Wait until the timing check is finished if necessary. */
  363.   replaydriver_timecheck_finish(obj->driver, obj->control, &time_check);
  364.  
  365.   /* Let the playback commence. */
  366.   replaydriver_play(obj->driver, obj->control);
  367.  
  368.   return 0; /* Return successfully. */
  369.  
  370. fail_in_soundtest:
  371.   /* If things fail during the sound timing test, stop the test first. */
  372.   replaydriver_timecheck_abort(obj->driver, obj->control, &time_check);
  373.   obj->playing = 0;
  374.  
  375. fail:
  376.   /* If things fail, free only the things internal to this function. */
  377.   replayer_free_driver_info(driver_info);
  378.   return rc;
  379. }
  380.  
  381. /* Feeds the sound driver with data, filling its buffer if necessary.
  382.    Return values are:
  383.       <0 -- something was done successfully
  384.      ==0 -- nothing needed to be done
  385.       >0 -- something was tried but went wrong
  386. */
  387. int replayer_sound_feed(replayer_file *obj)
  388. {
  389.   if(!obj || !obj->playing) return 1;
  390.  
  391.   /* Have we run out of chunks already? */
  392.   if(obj->current_chunk > obj->header->no_chunks ||
  393.      obj->current_chunk < 0) {
  394.     /* If the buffers are empty, the playback is finished. */
  395.     if(replaydriver_buffers_empty(obj->driver)) {
  396.       replayer_sound_stop(obj);
  397.       return replayer_feed_FINISHED;
  398.     }
  399.     /* Otherwise, let it finish. */
  400.     else return 0;
  401.   }
  402.  
  403.   /* Don't bother if no buffers need filling. */
  404.   if(replaydriver_is_hungry(obj->driver)) {
  405.     /* The variables we need. */
  406.     char *data;
  407.     int size; /* Size of chunk and buffer to allocate. */
  408.     replayer_chunk_entry *cchunk;
  409.     int offset, bytes_not_read, i;
  410.     int return_code;
  411.     int next_buffer; /* The buffer that will become empty after this. */
  412.  
  413.     /* Find the chunk and allocate the necessary space. */
  414.     cchunk = (replayer_chunk_entry *) ((char *) obj->chunks +
  415.     replayer_chunk_entry_SIZE(obj->header->no_soundtracks) *
  416.     obj->current_chunk);
  417.     size = cchunk->sound_size[obj->track_playing];
  418.     data = replaydriver_temp_alloc(obj->buffer, size);
  419.     if(!data) return replayer_feed_ALLOC_ERROR;
  420.  
  421.     /* Work out the offset and load the data. */
  422.     offset = cchunk->offset + cchunk->video_size;
  423.     for(i = 0; i < obj->track_playing; ++i) offset += cchunk->sound_size[i];
  424.     if(_swix(OS_GBPB, _IN(0)|_IN(1)|_IN(2)|_IN(3)|_IN(4)|_OUT(3),
  425.         3, obj->file, data, cchunk->sound_size[obj->track_playing],
  426.         offset, &bytes_not_read) || bytes_not_read != 0) {
  427.       /* Failed to read file correctly; return after freeing memory. */
  428.       return_code = replayer_feed_FILE_ERROR;
  429.       goto finish;
  430.     }
  431.  
  432.     /* If we're playing backwards, reverse the data. */
  433. #ifndef replayer_NO_BACKWARDS
  434.     if(obj->backwards) {
  435.       int sample_size;        /* Size of each sample in bytes. */
  436.       char *buffer;        /* Buffer for swapping samples. */
  437.       char *bottom, *top;    /* Samples to swap. */
  438.       replayer_soundtrack_info *info;
  439.  
  440.       info = &obj->header->soundtracks[obj->track_playing];
  441.       sample_size = info->precision * info->channels / 8;
  442.       buffer = malloc(sample_size);
  443.       if(!buffer) { return_code = replayer_feed_ALLOC_ERROR; goto finish; }
  444.       bottom = data;
  445.       /* size should be a multiple of sample_size, but just in case: */
  446.       top = data + size - (size % sample_size) - sample_size;
  447.  
  448.       /* Swap each pair of samples in turn. */
  449.       while(top > bottom) {
  450.         memcpy(buffer, top, sample_size);    /* temp = top */
  451.         memcpy(top, bottom, sample_size);    /* top = bottom */
  452.         memcpy(bottom, buffer, sample_size);    /* bottom = temp */
  453.  
  454.         /* Move onto next pair of samples. */
  455.         bottom += sample_size;
  456.         top -= sample_size;
  457.       }
  458.     }
  459. #endif
  460.  
  461.     /* Which buffer will become empty next?  The one that's full now
  462.        (they can't both be full). */
  463.     if(!*replaydriver_buffer_empty(obj->driver, 0)) next_buffer = 0;
  464.     else if(!*replaydriver_buffer_empty(obj->driver, 1)) next_buffer = 1;
  465.     else next_buffer = -1; /* Neither are full. */
  466.  
  467.     /* Fill the buffer. */
  468.     replaydriver_feed(obj->driver, obj->control, data,
  469.     cchunk->sound_size[obj->track_playing]);
  470.  
  471.     if(next_buffer == -1) {
  472.       /* Try again, which buffer will become empty next?  The one that has
  473.          just become full (only one must be full). */
  474.       if(!*replaydriver_buffer_empty(obj->driver, 0)) next_buffer = 0;
  475.       else if(!*replaydriver_buffer_empty(obj->driver, 1)) next_buffer = 1;
  476.     }
  477.     obj->next_buffer = next_buffer;
  478.  
  479.     /* Check whether the buffer has been overrun. */
  480.     /* Replay is braindead in this respect, as there is no way of telling
  481.        it what buffer size you calculated for it to check.  Of course,
  482.        MemCheck can't check up on what the driver is doing. */
  483.     if(replaydriver_buffer_check(obj->buffer)) {
  484.       fprintf(stderr,
  485.     "The check word in the sound buffer has been overwritten (buffer\n"
  486.     "was probably not big enough), so something important may have\n"
  487.     "been corrupted.  Now would be a good time to panic!  (But don't\n"
  488.     "worry, it's not your fault.)\n");
  489.       /* Return after freeing memory. */
  490.       return_code = replayer_feed_FATAL;
  491.       goto finish;
  492.     }
  493.  
  494.     /* Increment chunk counter, and drop through to finish. */
  495. #ifndef replayer_NO_BACKWARDS
  496.     if(obj->backwards) --obj->current_chunk;
  497.       else /* Drop through */
  498. #endif
  499.     ++obj->current_chunk;
  500.     return_code = replayer_feed_SUCCESS; /* Buffer filled successfully. */
  501.  
  502.     /* Free the data block and increment the chunk counter. */
  503.   finish:
  504.     replaydriver_temp_free(obj->buffer, data);
  505.     return return_code;
  506.   }
  507.   return 0; /* Nothing was done. */
  508. }
  509.  
  510. /* Returns a pointer to an integer which will become non-zero when a buffer
  511.    needs filling (this pointer will become invalid after playback is
  512.    stopped).  Used for pollword-nonzero handlers, and the TaskWindow's
  513.    equivalent.  Returns 0 if the facility doesn't work, or for failure. */
  514. const int *replayer_get_poll_word(replayer_file *obj)
  515. {
  516.   if(!obj || !obj->playing) return 0;
  517.   return replaydriver_buffer_empty(obj->driver, obj->next_buffer);
  518. }
  519.  
  520.  
  521. /* Preferences for playing sound. */
  522.  
  523. /* Set the sound quality, a value from 1--4 (higher is better quality).
  524.    Must be set before playback starts.  Default is maximum quality (4). */
  525. void replayer_set_sound_quality(replayer_file *obj, int quality /* 1--4 */)
  526. {
  527.   if(!obj) return;
  528.   obj->quality = quality;
  529. }
  530.  
  531. /* Tell the sound player whether to skip late data.  This must be set before
  532.    playback starts (if it is playing, it won't have an immediate effect).
  533.    Default is not to skip data. */
  534. void replayer_set_skip_late_data(replayer_file *obj, bool skip)
  535. {
  536.   if(!obj) return;
  537.   obj->skip_late_data = skip;
  538. }
  539.  
  540.  
  541. /* Methods for inflight entertainment. */
  542.  
  543. /* Pauses or unpauses the sound.  Returns non-zero if unsuccessful. */
  544. int replayer_sound_set_pause(replayer_file *obj, bool flag)
  545. {
  546.   if(!obj || !obj->playing) return 1;
  547.   if(flag) obj->control->data.flags |= replaydriver_PAUSE;
  548.       else obj->control->data.flags &=~ replaydriver_PAUSE;
  549.   return 0;
  550. }
  551.  
  552. /* Returns true if playback is paused. */
  553. bool replayer_sound_get_pause(const replayer_file *obj)
  554. {
  555.   if(!obj || !obj->playing) return 0;
  556.   return obj->control->data.flags & replaydriver_PAUSE ? 1:0;
  557. }
  558.  
  559. /* Mutes or un-mutes the sound.  Returns non-zero if unsuccessful. */
  560. int replayer_sound_set_mute(replayer_file *obj, bool flag)
  561. {
  562.   if(!obj || !obj->playing) return 1;
  563.   if(flag) obj->control->data.flags |= replaydriver_MUTE;
  564.       else obj->control->data.flags &=~ replaydriver_MUTE;
  565.   return 0;
  566. }
  567.  
  568. /* Returns true if playback is muted. */
  569. bool replayer_sound_get_mute(const replayer_file *obj)
  570. {
  571.   if(!obj || !obj->playing) return 0;
  572.   return obj->control->data.flags & replaydriver_MUTE ? 1:0;
  573. }
  574.  
  575. /* Returns the current time position of sound playing, in centiseconds since
  576.    playing started.  Gives -1 for an error. */
  577. int replayer_sound_get_time(const replayer_file *obj)
  578. {
  579.   if(!obj || !obj->playing) return -1;
  580.   return replaydriver_get_time(obj->driver, obj->control);
  581. }
  582.  
  583. /* Returns the total length in time of the Replay file in centiseconds.
  584.    Returns -1 for error. */
  585. int replayer_get_length(const replayer_file *obj)
  586. {
  587.   const replayer_header *header;
  588.   const replayer_chunk_entry *chunk;
  589.   const replayer_soundtrack_info *info;
  590.   const int track = 0;
  591.   int i, total_size = 0;
  592.   if(!obj) return -1; /* Fail. */
  593.  
  594.   /* This is done by going through each chunk and converting its size into
  595.      a time.  Track 0 is used (and why not?). */
  596.   header = replayer_get_header(obj);
  597.   if(!header || header->no_soundtracks < 1) return -1; /* Fail. */
  598.   chunk = replayer_get_chunk_catalogue(obj);
  599.   if(!chunk) return -1; /* Fail. */
  600.   info = &header->soundtracks[track];
  601.  
  602.   /* Go through each chunk in turn. */
  603.   for(i = header->no_chunks; i; --i) {
  604.     total_size += chunk->sound_size[track];
  605.     /* Next chunk! */
  606.     chunk = (replayer_chunk_entry *) ((char *) chunk +
  607.     replayer_chunk_entry_SIZE(header->no_soundtracks));
  608.   }
  609.  
  610.   /* Convert size to time and return. */
  611.   return (int) ((double) total_size /
  612.     /* Bytes per centisecond: */
  613.     (info->rate * info->precision * info->channels / 8 / 100));
  614. }
  615.  
  616. #ifndef replayer_NO_BACKWARDS
  617.  
  618. /* Sets the replayer object to play sound backwards.  Returns non-zero for
  619.    failure.  Cannot be changed while sound is playing. */
  620. int replayer_set_backwards(replayer_file *obj, bool flag)
  621. {
  622.   if(!obj || obj->playing) return 1; /* Fail */
  623.   obj->backwards = flag;
  624.   return 0; /* Success */
  625. }
  626.  
  627. /* Returns true if the object is set to play sound backwards. */
  628. bool replayer_get_backwards(const replayer_file *obj)
  629. {
  630.   return obj ? obj->backwards : 0;
  631. }
  632.  
  633. #endif /* !defined replayer_NO_BACKWARDS */
  634.  
  635.  
  636. /* Finalisation. */
  637.  
  638. /* Stops playback of sound, but does not call replayer_sound_tidy(), just
  639.    in case you want to resume playback again. */
  640. void replayer_sound_stop(replayer_file *obj)
  641. {
  642.   if(!obj || !obj->playing) return;
  643.  
  644.   replaydriver_stop(obj->driver, obj->control);
  645.   obj->playing = 0;
  646. }
  647.  
  648. /* Frees up any resources used by sound-only playing.  This is not called
  649.    when playback is stopped, but it will stop playback as a result of it
  650.    being called. */
  651. void replayer_sound_tidy(replayer_file *obj)
  652. {
  653.   if(!obj) return;
  654.   replayer_sound_stop(obj);
  655.  
  656.   /* Free the space used by the sound driver. */
  657.   if(obj->driver) {
  658.     replaydriver_destroy(obj->driver);
  659.     obj->driver = 0;
  660.   }
  661.   /* Free the sound control block. */
  662.   if(obj->control) {
  663.     replaydriver_control_destroy(obj->control);
  664.     obj->control = 0;
  665.   }
  666.   /* Free the sound data buffer. */
  667.   if(obj->buffer) {
  668.     replaydriver_buffer_destroy(obj->buffer);
  669.     obj->buffer = 0;
  670.   }
  671.   /* Close the input file. */
  672.   if(obj->file) {
  673.     _swix(OS_Find, _IN(0)|_IN(1), 0, obj->file);
  674.     obj->file = 0;
  675.   }
  676. }
  677.